//@author A0081229H
package storage;

import java.util.ArrayList;

public class DateRange implements Comparable<DateRange>, Clonable<DateRange>, Observer, Observable, Twinnable {
	private static final String SETSTARTDATE = "setStartDate";
	private static final String SETENDDATE = "setEndDate";

	private static final String ERROR_RANGE = "Start date cannot be later than end date.";

	private Date startDate, endDate;
	private ArrayList<Observer> observerList;
	Twinnable twin = null;

	// Constructors

	public DateRange(Date pStartDate, Date pEndDate) throws Exception {
		try{
			StorageLogger.log(this.getClass().getCanonicalName());
		} catch (Exception e) {

		}
		assert ((pStartDate == null) || (pEndDate == null)): Helper.ERROR_NULL;
		if (pStartDate.compareTo(pEndDate) == Helper.MORE) {
			throw new Exception(ERROR_RANGE);
		}
		startDate = pStartDate;
		startDate.addObs(this);
		endDate = pEndDate;
		endDate.addObs(this);
		observerList = new ArrayList<Observer>();
	}

	// Access Methods

	public Date getStartDate() {
		return startDate;
	}

	public Date getEndDate() {
		return endDate;
	}

	// Verification Methods

	public boolean isComplete() {
		if (!startDate.isEmpty() &&
				!endDate.isEmpty()) {
			return true;
		} else {
			return false;
		}
	}

	public boolean isEmpty() {
		if (startDate.isEmpty() &&
				endDate.isEmpty()) {
			return true;
		} else {
			return false;
		}
	}

	public boolean isDeadline() {
		if (startDate.isEmpty() &&
				!endDate.isEmpty()) {
			return true;
		} else {
			return false;
		}
	}

	public boolean isInverted() {
		if (startDate.compareTo(endDate) == Helper.MORE) {
			return true;
		} else {
			return false;
		}
	}

	// Modification Methods

	public boolean setStartDate(Date pDate) {
		try{
			StorageLogger.log(this.getClass().getCanonicalName() +
					Helper.SPACE + SETSTARTDATE + Helper.SPACE +
					pDate.getClass().getCanonicalName());
		} catch (Exception e) {

		}
		if (pDate == null) {
			return false;
		}
		if (isComplete() && pDate.compareTo(endDate) == Helper.MORE) {
			return false;
		}
		if (twin != null && isComplete() && twin.isComplete()) {
			if (twin.isInverted() && (pDate.compareTo(endDate) == Helper.SAME)) {
				return false;
			}
		}
		startDate = pDate;
		notifyObs();
		return true;
	}

	public boolean setEndDate(Date pDate) {
		try{
			StorageLogger.log(this.getClass().getCanonicalName() +
					Helper.SPACE + SETENDDATE + Helper.SPACE +
					pDate.getClass().getCanonicalName());
		} catch (Exception e) {

		}
		if (pDate == null) {
			return false;
		}
		if (isComplete() && pDate.compareTo(startDate) == Helper.LESS) {
			return false;
		}
		if (twin != null && isComplete() && twin.isComplete()) {
			if (twin.isInverted() && (pDate.compareTo(startDate) == Helper.SAME)) {
				return false;
			}
		}
		endDate = pDate;
		notifyObs();
		return true;
	}

	// Interfaces

	@Override
	public int compareTo(DateRange pDateRange) {
		return Helper.compareSameStyle(getStartDate().compareTo(pDateRange.getStartDate()),
				getEndDate().compareTo(pDateRange.getEndDate()));
	}

	@Override
	public DateRange cloneSelf() throws Exception {
		DateRange cloneDateRange;
		Date cloneStartDate = getStartDate().cloneSelf();
		Date cloneEndDate = getEndDate().cloneSelf();
		cloneDateRange = new DateRange(cloneStartDate, cloneEndDate);
		return cloneDateRange;
	}

	@Override
	public void update() {
		notifyObs();
	}

	@Override
	public void notifyObs() {
		for (Observer o : observerList) {
			o.update();
		}

	}

	@Override
	public void addObs(Observer o) {
		observerList.add(o);

	}

	@Override
	public void addTwin(Twinnable t) {
		twin = t;

	}

	@Override
	public boolean isSame() {
		return (startDate.compareTo(endDate) == Helper.SAME);
	}

	@Override
	public boolean equals(Object obj) {
		if(obj instanceof DateRange) {
			DateRange dateRange = (DateRange) obj;
			return this.startDate.equals(dateRange.startDate)
					&& this.endDate.equals(dateRange.endDate);
		}
		else {
			return false;
		}
	}
}
